﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;

namespace Wulong.Phone.Utils.Rx
{
    public static class ObservableExtensions
    {
        public static IObservable<TR> SelectAndSkipOnException<T, TR>(this IObservable<T> source, Func<T, TR> selector)
        {
            return
                source
                    .Select(t =>
                            Observable.Start(() => selector(t)).Catch(Observable.Empty<TR>()))
                    .Merge();
        }

        public static IObservable<Notification<TR>> Materialize<T, TR>(this IObservable<T> source, Func<T, TR> selector)
        {
            return source.Select(Notification.CreateOnNext).Materialize(selector);
        }

        public static IObservable<Notification<TR>> Materialize<T, TR>(
            this IObservable<Notification<T>> source, Func<T, TR> selector)
        {
            Func<Notification<T>, Notification<TR>> f = nt =>
                                                            {
                                                                if (nt.Kind == NotificationKind.OnNext)
                                                                {
                                                                    try
                                                                    {
                                                                        return
                                                                            Notification.CreateOnNext(
                                                                                selector(nt.Value));
                                                                    }
                                                                    catch (Exception ex)
                                                                    {
                                                                        ex.Data["Value"] = nt.Value;
                                                                        ex.Data["Selector"] = selector;
                                                                        return Notification.CreateOnError<TR>(ex);
                                                                    }
                                                                }
                                                                return nt.Kind == NotificationKind.OnError
                                                                           ? Notification.CreateOnError<TR>(nt.Exception)
                                                                           : Notification.CreateOnCompleted<TR>();
                                                            };
            return source.Select(f);
        }

        public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
        {
            var result = new ObservableCollection<T>();
            foreach (T item in source)
            {
                result.Add(item);
            } 
            return result;
        }
    }
}